var spinner_html = "<img class='spinner' src='http://www.google.com/ig/images/spinner.gif' width='16' height='16' />";
var invalid_str = "The requested URL <code>.*</code> was not found on this server.";
var invalid_re = new RegExp(invalid_str);
var invalidForm_str = "<center><form action='#'><table cellpadding='0' cellspacing='0'><tr><td align='left' style='font-size: 12px'>The specified project does not exist.<br />Please enter a valid project name:</td></tr><tr><td align='center'><input size='30' id='projectName' name='projectName' /></td></tr><tr><td align='right'><input type='submit' id='name_submit' value='Load Project'/></td></tr></table></form></center>"
var url_str = '';

// Used by the paging functions, these globals define certain paging events
var PAGE_PREV = -1;    // Event: goto previous page
var PAGE_NEXT = -2;    // Event: goto next page
var PAGE_MORE = -3;    // Event: open a new window with more

function adjustIFrameHeight() {
  try {
    gadgets.window.adjustHeight();
  } catch (e) {
    // do nothing.  We know the gadget may be running in environments that
    // do not support this feature (specifically third party pages, or outside
    // of IFRAMEs).
  }
}

function fetch(url, callback, type, opt_num_entries) {
  var params = {};
  params[gadgets.io.RequestParameters.CONTENT_TYPE] = type;
  if (opt_num_entries) {
    params[gadgets.io.RequestParameters.NUM_ENTRIES] = opt_num_entries;
  }

  //var nocache = 0;
  //url = [url, ((url.indexOf("?") > -1)?"&":"?"), "nocache=", nocache ].join("");

    gadgets.io.makeRequest(url, callback, params);
}

function fetchAsString(url, callback) {
  fetch(url, callback, gadgets.io.ContentType.TEXT);
}

function fetchAsDom(url, callback) {
  fetch(url, callback, gadgets.io.ContentType.DOM);
}

function fetchAsFeed(url, callback, opt_num_entries) {
  fetch(url, callback, gadgets.io.ContentType.FEED, opt_num_entries);
}

function fetchAsStringLegacy(url, callback) {
  fetchAsString(url, function (response) {   callback(response.text);  });
}

/**
 * Called on page load, it grabs the projectName preference and loads a page to see if it's valid.
 * This function expects a global, <code>url_tmpl</code> to be defined in the gadget specific js.
 */
function init() {
  // Add the loading animation
  $("#page_div").html("<div id='loading_div'>" + spinner_html + "</div>");

  // Get a new prefs obj
  prefs_obj = new gadgets.Prefs();

  // Get the project name from prefs
  projectName_str = prefs_obj.getString("projectName");

  // Check to see that it's not empty
  if (projectName_str == "") {
    printProjectForm();
    return;
  }

  // Construct the URL and fetch it
  url_str = url_tmpl.replace("%PROJECT%", projectName_str);
  fetchAsString(url_str, function (response) { projectLoaded(response.text); });
}

/**
 * Called by <code>init</code> after receiving a general project page.  This function detects
 * whether the project page, and therefore, the project, exists.
 *
 * @param response_str  The page.
*/
function projectLoaded(response_str) {
  // Check the retrieved page against a regular expression that will match if
  // Google Code could not find the page
  if (response_str.search(invalid_re) == -1) {
    // Valid Project
    $("#content").empty();  // Empty the loading animatino
    render(response_str);    // Call the gadget's render function
  } else {
    // Invalid Project
    printProjectForm();      // Print a form to ask for a project name
  }
}

/**
 * Called by projectLoaded if a project was not found for the specified project name.
 * This function asks for a new project name from the user.
 */
function printProjectForm() {
  // Print a form asking for a new project name
  $("#content > #loading_div").html(invalidForm_str);
  adjustIFrameHeight();

  // Add an event handler that will set some values and call init
  $("#name_submit").click(function() {
    // Set the project name locally, in preferences, and the title
    projectName_str = _esc($("#projectName").val());
    prefs_obj.set("projectName", projectName_str);
    gadgets.window.setTitle(title_tmpl.replace("%PROJECT%", projectName_str));

    // Call init to check existence
    init();
  })
}

/**
 * Given a google code page with paging info and a starting index, this will determine
 * the proper parameters for the <code>generatePaging</code> function.
 *
 * @param response_str  The page with paging info.
 * @param start_num     The starting index.
 * @returns             The paging links content string.
 */
function generatePagingFromPage(response_str, start_num) {
  // Set up RegEx and grab the pagination section of our data
  next_re = new RegExp(/<a href=".+">Next.*<\/a>/);
  prev_re = new RegExp(/<a href=".+">.*Prev<\/a>/);
  var pagination_str = jQuery(".pagination:first", jQuery(response_str)).html();
  var paging_str = "";
  var more_bool = false;
  var pages_num = 0;
  var totalItems_str = 0;
  var totalPages_num = 0;
  var page_num = 0;

  if (pagination_str == null) {
    return "";
  }

  // If we have a next or a prev link, then we'll need paging
  if (pagination_str.match(next_re) || pagination_str.match(prev_re)) {
    totalItems_str = new String(pagination_str.match(/.*of.*/)).replace(/.*of\ /, "");

    // Determine the number of pages
    totalPages_num = Math.ceil(totalItems_str / 10);

    // Limit to 4 pages, 40 entires
    pages_num = Math.min(totalPages_num, 4);

    // Determine our current page number
    page_num = Math.floor(start_num / 10) + 1;
  }

  // Flag for the more link, if necessary
  more_bool = page_num == pages_num && pages_num < totalPages_num;

  // Return the generated paging string, if there are more than the current page
  return pages_num > 0 ? generatePaging(pages_num, page_num, more_bool, totalItems_str) : "";
}

/**
 * Build a bit of HTML with links for performing paging actions.
 *
 * @param totalPages_num  The total number of pages.
 * @param currentPage_num The current page.
 * @param extraPages_bool Whether or not there are items beyond the page limit that
 *                        motivate a 'more' link.
 * @param totalItems_num  The total number of items for the 'See all XXX items' link
 * @returns               The paging links content string.
 */
function generatePaging(totalPages_num, currentPage_num, extraPages_bool, totalItems_num) {
  var paging_str = "";

  if (totalPages_num == 1) {
    return "";
  }

  // Construct the page number links.  This switch statement will match
  // the highest page number and fall through to concatenate all lower
  // pages as well.
  switch(totalPages_num) {
    case 4:
      paging_str = currentPage_num == 4 ? "4&nbsp;" + paging_str : "<a href='#' id='page4'>4</a>&nbsp;" + paging_str;
    case 3:
      paging_str = currentPage_num == 3 ? "3&nbsp;" + paging_str : "<a href='#' id='page3'>3</a>&nbsp;" + paging_str;
    case 2:
      paging_str = currentPage_num == 2 ? "2&nbsp;" + paging_str : "<a href='#' id='page2'>2</a>&nbsp;" + paging_str;
    case 1:
      paging_str = currentPage_num == 1 ? "1&nbsp;" + paging_str : "<a href='#' id='page1'>1</a>&nbsp;" + paging_str;
      break;
    default:
      break;
  }

  // Construct the prev link
  if (currentPage_num > 1) {
    paging_str = "<a href='#' id='prev'>Previous</a>&nbsp;" + paging_str;
  }

  // Construct the next or more link
  if (currentPage_num < totalPages_num) {
    // If the current page is less than all our pages, print the next
    paging_str += "<a href='#' id='next'>Next</a>&nbsp;";
  } else if (currentPage_num == totalPages_num && extraPages_bool) {
    // If we're at the last page, and there are more total pages, provide a jump
    //paging_str += "<a href='#' id='more'>more</a>"
    paging_str += "<br /><a href='#' id='more'>See all items</a>"
  }

  // Return the built HTML
  return paging_str;
}

/**
 * Adds an event handler to each paging link.
 *
 * @param pageCallback_fn The function to call to perform paging actions.
 */
function addPagingEvents(pageCallback_fn) {
  // There's gotta be a faster way of doing this
  // jQuery only likes to add the event to the first item
  // each() doesn't work either
  $("#prev").click(function(event) {
    event.preventDefault();       // Prevent link from doing something else
    pageCallback_fn(PAGE_PREV);   // Call the init function to reload the data
  });
  $("#next").click(function(event) {
    event.preventDefault();
    pageCallback_fn(PAGE_NEXT);
  });

  $("#page4").click(function(event) {
    event.preventDefault();
    pageCallback_fn(4);
  });

  $("#page3").click(function(event) {
    event.preventDefault();
    pageCallback_fn(3);
  });

  $("#page2").click(function(event) {
    event.preventDefault();
    pageCallback_fn(2);
  });

  $("#page1").click(function(event) {
    event.preventDefault();
    pageCallback_fn(1);
  });

  $("#more").click(function(event) {
    event.preventDefault();
    pageCallback_fn(PAGE_MORE);
  })
}

/**
 * If the given string is greater than the given length, it will be truncated
 * and ellipses will be add on the end.
 *
 * @param str The string to ellipsify
 * @param length_num The length to restrict it to
 * @returns The whole or ellipsified word
 */
function ellipsify(str, length_num) {
  if (str.length <= length_num) {
    return str;
  }

  return str.substr(0, length_num).concat("...");
}

/**
 * Ellipsify any words in the given text that exceed the given length.
 *
 * @param str The string to scan
 * @param length The maximum length for any one word
 * @returns The string with all applicable words ellipsified
 */
function ellipsifyWords(str, length_num) {
  var i;

  // First split the text into words
  words_arr = str.split(" ");

  // Ellipsify each word
  for (i = 0; i < words_arr.length; i++) {
    words_arr[i] = ellipsify(words_arr[i], length_num);
  }

  // Join the words back together
  return words_arr.join(" ");
}
